/*
 * Copyright 2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.seppiko.chart.controllers;

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import org.seppiko.chart.models.BarcodeEntity;
import org.seppiko.chart.models.ControllerEntity;
import org.seppiko.chart.models.MessageEntity;
import org.seppiko.chart.services.QrCodeService;
import org.seppiko.chart.utils.JsonUtil;
import org.seppiko.chart.utils.ResponseUtil;
import org.seppiko.chart.utils.StringUtils;
import org.seppiko.chart.utils.ZxingImageUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Leonard Woo
 */
@RestController
public class QrController {

  @Autowired
  private QrCodeService service;

  @GetMapping(value = "/qr")
  public ResponseEntity<byte[]> qrGetContentHandleExecution(
      @RequestParam(value = "data", required = false, defaultValue = "SeppikoChart") String data,
      @RequestParam(value = "encoding", required = false, defaultValue = "ISO-8859-1") String encoding,
      @RequestParam(value = "eclevel", required = false, defaultValue = "L") String ecLevel,
      @RequestParam(value = "size", required = false, defaultValue = "200x200") String size,
      @RequestParam(value = "margin", required = false, defaultValue = "1") String margin,
      @RequestParam(value = "bgcolor", required = false, defaultValue = "0xFFFFFF") String bgcolor,
      @RequestParam(value = "color", required = false, defaultValue = "0x000000") String color,
      @RequestParam(value = "format", required = false, defaultValue = "png") String format) {

    BarcodeEntity entity = new BarcodeEntity();

    entity.setEncoding(encoding);
    try {
      entity.setData(StringUtils.urlDecode(data, encoding));
    } catch (UnsupportedEncodingException e) {
      entity.setData(StringUtils.urlDecode(data, StandardCharsets.UTF_8));
      entity.setEncoding("UTF-8");
    }

    entity.setErrorCorrectionLevel(ecLevel);

    String[] sizes = size.split("x");
    entity.setWidth(Integer.decode(sizes[0]));
    entity.setHeight(Integer.decode(sizes[1]));

    entity.setMargin(Integer.decode(margin));
    entity.setBackgroundColor(bgcolor);
    entity.setColor(color);


    format = StringUtils.urlDecode(format, StandardCharsets.UTF_8);
    entity.setNeedBase64(false);
    if (format.contains("|")) {
      String[] f = format.split("\\|");
      format = f[0];
      entity.setNeedBase64(f[1].equalsIgnoreCase("base64"));
    }
    if (format.equalsIgnoreCase("json")) {
      entity.setEnableJson(true);
      entity.setFormat("png");
    } else {
      entity.setFormat(format.toLowerCase());
    }

    ControllerEntity resp = new ControllerEntity();

    if ( (entity.getEnableJson() && entity.getNeedBase64()) ||
        (entity.getFormat().matches("t[e]?xt") &&
            (entity.getEnableJson() || entity.getNeedBase64()) ) ) {
      resp.setStatus(400);
      resp.setMediaType(MediaType.APPLICATION_JSON);
      resp.setData( JsonUtil.toJson(
          new MessageEntity(400, "Only image format have |base64 or |json") ).getBytes() );
    } else {
      resp = service.qrCodeCHX(entity);
    }

    return ResponseUtil.sendResponse( resp );
  }

  @PostMapping(value = "/qr")
  public ResponseEntity<byte[]> qrPostContentHandleExecution (@RequestBody String requestStr) {
    if (StringUtils.isEmpty(requestStr)) {
//      requestStr = "{}";
      return ResponseUtil.sendResponse( 400, MediaType.APPLICATION_JSON,
          JsonUtil.toJson( new MessageEntity(400, "Bad request, because JSON is {}.") ).getBytes() );
    }
    BarcodeEntity entity = JsonUtil.fromJson(requestStr, BarcodeEntity.class);
    return ResponseUtil.sendResponse( service.qrCodeCHX(entity) );
  }

  @GetMapping(value = "/qrd")
  public ResponseEntity<byte[]> qrdGetContentHandleExecution(
      @RequestParam(value = "data", required = false, defaultValue = "SeppikoChart") String data,
      @RequestParam(value = "type", required = false, defaultValue = "0") Integer type,
      @RequestParam(value = "size", required = false, defaultValue = "1") Integer size,
      @RequestParam(value = "margin", required = false, defaultValue = "2") Integer margin,
      @RequestParam(value = "level", required = false, defaultValue = "h") String level,
      @RequestParam(value = "bgcolor", required = false, defaultValue = "0xFFFFFF") String bgcolor,
      @RequestParam(value = "color", required = false, defaultValue = "0x000000") String color,
      @RequestParam(value = "format", required = false, defaultValue = "gif") String format) {

    BarcodeEntity entity = new BarcodeEntity();

    entity.setData(StringUtils.urlDecode(data, StandardCharsets.UTF_8));
    entity.setErrorCorrectionLevel(level);
    entity.setType(type);
    entity.setSize(size);
    entity.setMargin(margin);

    entity.setBackgroundColor(bgcolor);
    entity.setColor(color);

    entity.setFormat(StringUtils.urlDecode(format, StandardCharsets.UTF_8).toLowerCase());

    ControllerEntity resp = new ControllerEntity();

    if ( !( hasBetweenValue(entity.getType(), 0, 9)
        || hasBetweenValue(entity.getSize(), 1, 4)
        || hasBetweenValue(entity.getMargin(), 0, 31) ) ) {
      resp.setStatus(400);
      resp.setMediaType(MediaType.APPLICATION_JSON);
      resp.setData( JsonUtil.toJson(
          new MessageEntity(400, "illegal number") ).getBytes() );
    } else {
      Integer bgColor_num = Integer.decode(entity.getBackgroundColor());
      Integer color_num = Integer.decode(entity.getColor());
      boolean flag = (bgColor_num.equals(ZxingImageUtil.WHITE)) &&
          (color_num.equals(ZxingImageUtil.BLACK));

      resp = service.qrCodeDCHX(entity, flag);
    }

    return ResponseUtil.sendResponse( resp );
  }

  private boolean hasBetweenValue(Integer value, Integer min, Integer max) {
    return value >= min && value <= max;
  }

  @PostMapping(value = "/qrd")
  public ResponseEntity<byte[]> qrdPostContentHandleExecution (@RequestBody String requestStr) {
    if (StringUtils.isEmpty(requestStr)) {
      return ResponseUtil.sendResponse( 400, MediaType.APPLICATION_JSON,
          JsonUtil.toJson( new MessageEntity(400, "Bad request, because JSON is {}.") ).getBytes() );
    }
    BarcodeEntity entity = JsonUtil.fromJson(requestStr, BarcodeEntity.class);
    Integer bgColor_num = Integer.decode(entity.getBackgroundColor());
    Integer color_num = Integer.decode(entity.getColor());
    boolean flag = (bgColor_num.equals(ZxingImageUtil.WHITE)) &&
        (color_num.equals(ZxingImageUtil.BLACK));
    return ResponseUtil.sendResponse( service.qrCodeDCHX(entity, flag) );
  }

  @GetMapping(value = "/qrx")
  public ResponseEntity<byte[]> qrxGetContentHandleExecution(
      @RequestParam(value = "data", required = false, defaultValue = "SeppikoChart") String data,
      @RequestParam(value = "encoding", required = false, defaultValue = "ISO-8859-1") String encoding,
      @RequestParam(value = "eclevel", required = false, defaultValue = "L") String ecLevel,
      @RequestParam(value = "size", required = false, defaultValue = "200x200") String size,
      @RequestParam(value = "margin", required = false, defaultValue = "1") String margin,
      @RequestParam(value = "bgcolor", required = false, defaultValue = "0xFFFFFF") String bgcolor,
      @RequestParam(value = "color", required = false, defaultValue = "0x000000") String color,
      @RequestParam(value = "format", required = false, defaultValue = "png") String format) {

    BarcodeEntity entity = new BarcodeEntity();

    entity.setEncoding(encoding);
    try {
      entity.setData(StringUtils.urlDecode(data, encoding));
    } catch (UnsupportedEncodingException e) {
      entity.setData(StringUtils.urlDecode(data, StandardCharsets.UTF_8));
      entity.setEncoding("UTF-8");
    }

    entity.setErrorCorrectionLevel(ecLevel);

    String[] sizes = size.split("x");
    entity.setWidth(Integer.decode(sizes[0]));
    entity.setHeight(Integer.decode(sizes[1]));

    entity.setMargin(Integer.decode(margin));
    entity.setBackgroundColor(bgcolor);
    entity.setColor(color);


    format = StringUtils.urlDecode(format, StandardCharsets.UTF_8);
    entity.setNeedBase64(false);
    if (format.contains("|")) {
      String[] f = format.split("\\|");
      format = f[0];
      entity.setNeedBase64(f[1].equalsIgnoreCase("base64"));
    }
    if (format.equalsIgnoreCase("json")) {
      entity.setEnableJson(true);
      entity.setFormat("png");
    } else {
      entity.setFormat(format.toLowerCase());
    }

    ControllerEntity resp = new ControllerEntity();

    if ( (entity.getEnableJson() && entity.getNeedBase64()) ||
        (entity.getFormat().matches("t[e]?xt") &&
            (entity.getEnableJson() || entity.getNeedBase64()) ) ) {
      resp.setStatus(400);
      resp.setMediaType(MediaType.APPLICATION_JSON);
      resp.setData( JsonUtil.toJson(
          new MessageEntity(400, "Only image format have |base64 or |json") ).getBytes() );
    } else {
      resp = service.qrCodeXCHX(entity);
    }

    return ResponseUtil.sendResponse( resp );
  }

  @PostMapping(value = "/qrx")
  public ResponseEntity<byte[]> qrxPostContentHandleExecution (@RequestBody String requestStr) {
    if (StringUtils.isEmpty(requestStr)) {
      return ResponseUtil.sendResponse( 400, MediaType.APPLICATION_JSON,
          JsonUtil.toJson( new MessageEntity(400, "Bad request, because JSON is {}.") ).getBytes() );
    }
    BarcodeEntity entity = JsonUtil.fromJson(requestStr, BarcodeEntity.class);
    return ResponseUtil.sendResponse( service.qrCodeXCHX(entity) );
  }
}
